package top.fyr.aliyunsms;


import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import sun.misc.BASE64Encoder;
import top.fyr.aliyunsms.model.QuerySendSmsDetailRequest;
import top.fyr.aliyunsms.model.SendBatchSmsRequest;
import top.fyr.aliyunsms.model.SendSmsDetail;
import top.fyr.aliyunsms.model.SendSmsResponse;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 阿里云短信发送
 *
 * @author fyr
 * @date 2020-09-23 023
 */
@Slf4j
public class SmsSender {

    public static SendSmsResponse sendBatchSms(SendBatchSmsRequest request) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("调用阿里云短信发送接口请求参数：{}", request);
        }
        Map<String, String> variables = new HashMap<>(8);
        variables.put("Action", "SendBatchSms");
        variables.put("Version", "2017-05-25");
        variables.put("RegionId", request.getRegionId());
        variables.put("PhoneNumberJson", request.getPhoneNumberJson());
        variables.put("SignNameJson", request.getSignNameJson());
        variables.put("TemplateParamJson", request.getTemplateParamJson());
        variables.put("TemplateCode", request.getTemplateCode());
        variables.put("OutId", "123");
        // 最终得到合法GET请求的URL
        String uri = generateUrl(request.getAccessKeyId(), request.getAccessKeySecret(), request.getServiceUrl(), variables);
        SendSmsResponse sendSmsResponse;
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(uri);
            CloseableHttpResponse response = httpclient.execute(httpGet);
            if (log.isDebugEnabled()) {
                log.debug("http调用阿里云发送短信接口返回：{}", response);
            }
            HttpEntity entity = response.getEntity();
            sendSmsResponse = JSONObject.parseObject(EntityUtils.toString(entity), SendSmsResponse.class);
            if (log.isInfoEnabled()) {
                log.info("调用阿里云短信发送接口响应内容：{}", sendSmsResponse);
            }
        }
        return sendSmsResponse;
    }


    public static SendSmsDetail querySendDetails(QuerySendSmsDetailRequest request) throws Exception {
        if (log.isDebugEnabled()) {
            log.debug("调用阿里云查询短信发送详情接口请求参数：{}", request);
        }
        Map<String, String> variables = new HashMap<>(8);
        variables.put("Action", "QuerySendDetails");
        variables.put("Version", "2017-05-25");
        variables.put("RegionId", request.getRegionId());
        variables.put("CurrentPage", "1");
        variables.put("PageSize", "1");
        variables.put("PhoneNumber", request.getPhoneNumber());
        variables.put("SendDate", request.getSendDate());
        variables.put("BizId", request.getBizId());

        // 最终得到合法GET请求的URL
        String uri = generateUrl(request.getAccessKeyId(), request.getAccessKeySecret(), request.getServiceUrl(), variables);
        SendSmsDetail sendSmsDetail;
        try (CloseableHttpClient httpclient = HttpClients.createDefault()) {
            HttpGet httpGet = new HttpGet(uri);
            CloseableHttpResponse response = httpclient.execute(httpGet);
            if (log.isDebugEnabled()) {
                log.debug("http调用阿里云查询短信发送详情接口返回：{}", response);
            }
            HttpEntity entity = response.getEntity();
            String toString = EntityUtils.toString(entity);
            sendSmsDetail = JSONObject.parseObject(toString, SendSmsDetail.class);
            if (log.isInfoEnabled()) {
                log.info("调用阿里云查询短信发送详情接口响应内容：{}", sendSmsDetail);
            }
        }
        return sendSmsDetail;
    }


    private static String generateUrl(String accessKeyId, String accessSecret, String serviceUrl, Map<String, String> variables) throws Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'");
        // 这里一定要设置GMT时区
        df.setTimeZone(new SimpleTimeZone(0, "GMT"));

        Map<String, String> paras = new HashMap<>(16);
        // 1. 系统参数
        paras.put("SignatureMethod", "HMAC-SHA1");
        paras.put("SignatureNonce", UUID.randomUUID().toString());
        paras.put("AccessKeyId", accessKeyId);
        paras.put("SignatureVersion", "1.0");
        paras.put("Timestamp", df.format(new Date()));
        paras.put("Format", "JSON");

        // 2. 业务API参数
        variables.forEach(paras::put);

        // 3. 去除签名关键字Key
        if (paras.containsKey("Signature")) {
            paras.remove("Signature");
        }

        // 4. 参数KEY排序
        TreeMap<String, String> sortParas = new TreeMap<>(paras);

        // 5. 构造待签名的字符串
        Iterator<String> it = sortParas.keySet().iterator();
        StringBuilder sortQueryStringTmp = new StringBuilder();
        while (it.hasNext()) {
            String key = it.next();
            sortQueryStringTmp.append("&").append(specialUrlEncode(key)).append("=").append(specialUrlEncode(paras.get(key)));
        }
        // 去除第一个多余的&符号
        String sortedQueryString = sortQueryStringTmp.substring(1);

        StringBuilder stringToSign = new StringBuilder();
        stringToSign.append("GET").append("&");
        stringToSign.append(specialUrlEncode("/")).append("&");
        stringToSign.append(specialUrlEncode(sortedQueryString));

        String sign = sign(accessSecret + "&", stringToSign.toString());
        // 6. 签名最后也要做特殊URL编码
        String signature = specialUrlEncode(sign);

        // 最终得到合法GET请求的URL
        String uri = serviceUrl + signature + sortQueryStringTmp;
        if (log.isDebugEnabled()) {
            log.debug("发送阿里云短信请求url:{}", uri);
        }
        return uri;
    }

    private static String specialUrlEncode(String value) throws Exception {
        return URLEncoder.encode(value, "UTF-8").replace("+", "%20").replace("*", "%2A").replace("%7E", "~");
    }

    private static String sign(String accessSecret, String stringToSign) throws Exception {
        Mac mac = Mac.getInstance("HmacSHA1");
        mac.init(new SecretKeySpec(accessSecret.getBytes(StandardCharsets.UTF_8), "HmacSHA1"));
        byte[] signData = mac.doFinal(stringToSign.getBytes(StandardCharsets.UTF_8));
        return new BASE64Encoder().encode(signData);
    }
}
